package api;

import com.fasterxml.jackson.databind.ObjectMapper;
import common.CodeInValidException;
import common.ProblemNotFoundException;
import compile.Answer;
import compile.Question;
import compile.Task;
import dao.Problem;
import dao.ProblemDAO;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

/**
 * created by YT
 * description: 编译运行，将编译结果返回给前端
 * User:lenovo
 * Data:2022-07-24
 * Time:12:54
 */
@WebServlet("/compile")
public class CompileServlet extends HttpServlet {

    static class CompileRequest {
        public int id;
        public String code;
    }

    static class CompileResponse {
        public int error;
        public String reason;
        public String stdout;
    }

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(System.getProperty("user.dir"));
        resp.setStatus(200);
        resp.setContentType("application/json;charset=utf8");
        CompileResponse compileResponse = new CompileResponse();
        try {
            // 1. 读取请求的正文，并且按照 json格式进行解析
            String body = readBody(req);
            CompileRequest compileRequest = objectMapper.readValue(body,CompileRequest.class);
            // 黑名单校验代码安全性
            boolean flag = checkCodeSafe(compileRequest.code);
            if (!flag) {
                compileResponse.error = -1;
                compileResponse.reason = "代码不安全";
                return;
            }

            Problem problem = ProblemDAO.findProblem(compileRequest.id);
            if (problem == null) {
                throw new ProblemNotFoundException();
            }
                // 2. 读取数据，将获得的数据(得到测试用例代码)，将数据进行拼接
                //    用于测试的代码
            String testCode = problem.getTestCode();
            //    用户发来的代码
            String requestCode = compileRequest.code;
            //    构造最终代码
            String finalCode = mergeCode(testCode, requestCode);
            if (finalCode == null) {
                throw new CodeInValidException();
            }
            //System.out.println(finalCode);

            // 3. 创建一个实例，调用里面的compileAndRun 来进行编译运行
            Task task = new Task();
            Question question = new Question();
            question.setCode(finalCode);
            // 获得到编译运行的结果
            Answer answer = task.compileAndRun(question);
            // 4. 根据运行结果包装成一个Http响应
            compileResponse.error = answer.getError();
            compileResponse.reason = answer.getReason();
            compileResponse.stdout = answer.getStdout();
            //将结果json格式化
        } catch (ProblemNotFoundException e) {
            compileResponse.error = -1; // 表示未找到题目
            compileResponse.reason = "没有找到指定题目";
        } catch (CodeInValidException e) {
            compileResponse.error = -1;
            compileResponse.reason = "提交的代码不合法";
        } finally {
            String returnString = objectMapper.writeValueAsString(compileResponse);
            resp.getWriter().write(returnString);
        }
    }

    /**
    * @Author: YT摆渡人
    * @Description: 校验代码安全性
    * @DateTime: 2022/7/25 13:13
    * @Params: 用户输入代码
    * @Return 是否合法
    */

    private static boolean checkCodeSafe(String code) {
        List<String> blackList = new ArrayList<>();
        blackList.add("Runtime");
        blackList.add("exec");
        blackList.add("java.io");
        blackList.add("java.net");
        for (String blackString : blackList) {
            if (code.indexOf(blackString) >= 0) {
                return false;
            }
        }
        return true;
    }

    /**
    * @Author: YT摆渡人
    * @Description: 拼接代码
    * @DateTime: 2022/7/24 14:12
    * @Params: 测试代码和请求中的代码
    * @Return 拼接后用于编译的代码
    */

    private static String mergeCode(String testCode, String requestCode) {
        int index = requestCode.lastIndexOf("}");
        if (index == -1) {
            // 非法代码
            return null;
        }
        String code1 = requestCode.substring(0,index);

        String finalCode = code1 + testCode + "\n}";

        return finalCode;
    }

    private static String readBody(HttpServletRequest req) throws UnsupportedEncodingException {
        int bodyLen = req.getContentLength();
        byte[] bodyByte = new byte[bodyLen];
        try (InputStream inputStream = req.getInputStream()) {
            inputStream.read(bodyByte);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String body = new String(bodyByte, "utf8");
        return body;
    }
}
